﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.IO;
using System.Linq.Expressions;
using System.Reflection;
using System.Security;
using System.Security.Policy;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using vm.Aspects.Diagnostics;
using vm.Aspects.Tests.PartialTrustStub;

namespace vm.Aspects.Tests.Diagnostics
{
    [TestClass]
    public class ObjectTextDumperTest
    {
        public TestContext TestContext { get; set; }

        PrivateObject GetDumperInstanceAccessor(int indentLevel = 0, int indentLength = 2)
        {
            return new PrivateObject(typeof(ObjectTextDumper), new StringWriter(CultureInfo.InvariantCulture), indentLevel, indentLength);
        }

        PrivateObject GetDumperInstanceAccessor(StringWriter w, int indentLevel = 0, int indentLength = 2)
        {
            return new PrivateObject(typeof(ObjectTextDumper), w, indentLevel, indentLength);
        }

        PrivateType GetDumperClassAccessor()
        {
            return new PrivateType(typeof(ObjectTextDumper));
        }

        [TestMethod]
        [ExpectedException(typeof(TargetInvocationException))]
        public void TestConstructor_Null()
        {
            try
            {
                new PrivateObject(typeof(ObjectTextDumper), null, 0, 0);
            }
            catch (TargetInvocationException x)
            {
                Assert.IsInstanceOfType(x.InnerException, typeof(ArgumentNullException));
                throw;
            }
        }

        enum TestEnum
        {
            None,
            One,
            Two,
            All,
        }

        #region basic values and corresponding strings
        object[] basicValues =
        {
            null,
            new int?(),
            true,
            'A',
            (byte)1,
            (sbyte)1,
            (short)1,
            (int)1,
            (long)1,
            (ushort)1,
            (uint)1,
            (ulong)1,
            1.0,
            (float)1.0,
            1M,
            "1M",
            Guid.Empty,
            new Uri("http://localhost"),
            new DateTime(2013, 1, 13),
            new TimeSpan(123L),
            new DateTimeOffset(new DateTime(2013, 1, 13)),
        };

        string[] basicValuesStrings =
        {
            "<null>",
            "<null>",
            "True",
            "A",
            "1",
            "1",
            "1",
            "1",
            "1",
            "1",
            "1",
            "1",
            "1",
            "1",
            "1",
            "1M",
            "00000000-0000-0000-0000-000000000000",
            "http://localhost/",
            "2013-01-13T00:00:00.0000000",
            "00:00:00.0000123",
            "2013-01-13T00:00:00.0000000-05:00",
        };
        #endregion

        [TestMethod]
        public void TestIsBasicType()
        {
            var target = GetDumperClassAccessor();

            for (var i=2; i<basicValues.Length; i++)
                Assert.IsTrue(basicValues[i].GetType().IsBasicType());
        }

        [TestMethod]
        public void TestDumpedBasicValue()
        {
            var target = GetDumperInstanceAccessor();

            Assert.IsFalse((bool)target.Invoke("DumpedBasicValue", this, null));
            foreach (var v in basicValues)
                Assert.IsTrue((bool)target.Invoke("DumpedBasicValue", v, null));
        }

        void TestDumpedBasicValueText(
            string expected,
            object value,
            DumpAttribute dumpAttribute = null,
            int indentValue = 0)
        {
            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var target = GetDumperInstanceAccessor(w, indentValue);

                Assert.IsTrue((bool)target.Invoke("DumpedBasicValue", value, dumpAttribute));

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        void TestDumpObjectBasicValueText(
            string expected,
            object value,
            Type metadata = null,
            DumpAttribute dumpAttribute = null,
            int indentValue = 0)
        {
            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var target = GetDumperInstanceAccessor(w, indentValue);

                Assert.AreEqual(target.Target, target.Invoke("Dump", value, metadata, dumpAttribute));

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        [TestMethod]
        public void TestDumpedBasicValueText()
        {
            for (var i=0; i<basicValues.Length; i++)
                TestDumpedBasicValueText(basicValuesStrings[i], basicValues[i]);
        }

        [TestMethod]
        public void TestDumpedBasicValueTextIndent()
        {
            for (var i=0; i<basicValues.Length; i++)
                TestDumpedBasicValueText(basicValuesStrings[i], basicValues[i], null, 2);
        }

        [TestMethod]
        public void TestDumpMaskedBasicValueText()
        {
            for (var i=0; i<basicValues.Length; i++)
                TestDumpedBasicValueText(basicValues[i]==null ? "<null>" : "------", basicValues[i], new DumpAttribute { Mask = true, MaskValue = "------" });
        }

        [TestMethod]
        public void TestDumpMaskedBasicValueText1()
        {
            for (var i=0; i<basicValues.Length; i++)
                TestDumpedBasicValueText(basicValues[i]==null ? "<null>" : "******", basicValues[i], new DumpAttribute { Mask = true });
        }

        [TestMethod]
        public void TestDumpedBasicValueFormat()
        {
            TestDumpedBasicValueText("{00000000-0000-0000-0000-000000000000}", Guid.Empty, new DumpAttribute { ValueFormat = "{0:B}" });
            TestDumpedBasicValueText("¤1.00", 1.0, new DumpAttribute { ValueFormat = "{0:C}" });
            TestDumpedBasicValueText("¤1.00", 1M, new DumpAttribute { ValueFormat = "{0:C}" });
        }

        [TestMethod]
        public void TestStringValueLength()
        {
            TestDumpedBasicValueText("012345678901...", "01234567890123456789", new DumpAttribute { MaxLength = 12 });
        }

        [TestMethod]
        public void TestDumpObjectBasicValueText()
        {
            for (var i=0; i<basicValues.Length; i++)
                TestDumpObjectBasicValueText(basicValuesStrings[i], basicValues[i]);
        }

        [TestMethod]
        public void TestDumpObjectBasicValueTextIndent()
        {
            for (var i=0; i<basicValues.Length; i++)
                TestDumpObjectBasicValueText(basicValuesStrings[i], basicValues[i], null, null, 2);
        }

        [TestMethod]
        public void TestDumpObjectMaskedBasicValueText()
        {
            for (var i=0; i<basicValues.Length; i++)
                TestDumpObjectBasicValueText(basicValues[i]==null ? "<null>" : "------", basicValues[i], null, new DumpAttribute { Mask = true, MaskValue = "------" });
        }

        [TestMethod]
        public void TestDumpObjectMaskedBasicValueText1()
        {
            for (var i=0; i<basicValues.Length; i++)
                TestDumpObjectBasicValueText(basicValues[i]==null ? "<null>" : "******", basicValues[i], null, new DumpAttribute { Mask = true });
        }

        [TestMethod]
        public void TestDumpObjectBasicValueFormat()
        {
            TestDumpObjectBasicValueText("{00000000-0000-0000-0000-000000000000}", Guid.Empty, null, new DumpAttribute { ValueFormat = "{0:B}" });
            TestDumpObjectBasicValueText("¤1.00", 1.0, null, new DumpAttribute { ValueFormat = "{0:C}" });
            TestDumpObjectBasicValueText("¤1.00", 1M, null, new DumpAttribute { ValueFormat = "{0:C}" });
        }

        [TestMethod]
        public void TestObjectStringValueLength()
        {
            TestDumpObjectBasicValueText("012345678901...", "01234567890123456789", null, new DumpAttribute { MaxLength = 12 });
        }

        public class Object1
        {
            public object ObjectProperty { get; set; }
            public int? NullIntProperty { get; set; }
            public long? NullLongProperty { get; set; }
            public bool BoolProperty { get; set; }
            public char CharProperty { get; set; }
            public byte ByteProperty { get; set; }
            public sbyte SByteProperty { get; set; }
            public short ShortProperty { get; set; }
            public int IntProperty { get; set; }
            public long LongProperty { get; set; }
            public ushort UShortProperty { get; set; }
            public uint UIntProperty { get; set; }
            public ulong ULongProperty { get; set; }
            public double DoubleProperty { get; set; }
            public float FloatProperty { get; set; }
            public decimal DecimalProperty { get; set; }
            public Guid GuidProperty { get; set; }
            public Uri UriProperty { get; set; }
            public DateTime DateTimeProperty { get; set; }
            public TimeSpan TimeSpanProperty { get; set; }
            public DateTimeOffset DateTimeOffsetProperty { get; set; }
        }

        class Object1Metadata
        {
            private Object1Metadata() { }

            [Dump(0, LabelFormat="{0,-24} : ")]
            public object ObjectProperty { get; set; }
            [Dump(1)]
            public object NullIntProperty { get; set; }
            [Dump(2)]
            public object NullLongProperty { get; set; }
            [Dump(3)]
            public object BoolProperty { get; set; }
            [Dump(4)]
            public object CharProperty { get; set; }
            [Dump(5)]
            public object ByteProperty { get; set; }
            [Dump(6)]
            public object SByteProperty { get; set; }
            [Dump(7)]
            public object ShortProperty { get; set; }
            [Dump(8)]
            public object IntProperty { get; set; }
            [Dump(9)]
            public object LongProperty { get; set; }
            [Dump(false)]
            public object UShortProperty { get; set; }
            [Dump(-1)]
            public object UIntProperty { get; set; }
            [Dump(-2)]
            public object ULongProperty { get; set; }
            [Dump(-3, ValueFormat="{0:F01}")]
            public object DoubleProperty { get; set; }
            [Dump(-4, ValueFormat="{0:F01}")]
            public object FloatProperty { get; set; }
            [Dump(-5)]
            public object DecimalProperty { get; set; }
            [Dump(-6)]
            public object GuidProperty { get; set; }
            [Dump(-7)]
            public object UriProperty { get; set; }
            [Dump(-8)]
            public object DateTimeProperty { get; set; }
            [Dump(-9)]
            public object TimeSpanProperty { get; set; }
            [Dump(false)]
            public object DateTimeOffsetProperty { get; set; }
        }

        [Dump(DumpNullValues=ShouldDump.Skip)]
        public class Object2
        {
            [Dump(0, LabelFormat="{0,-24} : ")]
            public object ObjectProperty { get; set; }
            [Dump(1)]
            public int? NullIntProperty { get; set; }
            [Dump(2)]
            public long? NullLongProperty { get; set; }
            [Dump(3)]
            public bool BoolProperty { get; set; }
            [Dump(4)]
            public char CharProperty { get; set; }
            [Dump(5)]
            public byte ByteProperty { get; set; }
            [Dump(6)]
            public sbyte SByteProperty { get; set; }
            [Dump(7)]
            public short ShortProperty { get; set; }
            [Dump(8)]
            public int IntProperty { get; set; }
            [Dump(9)]
            public long LongProperty { get; set; }
            [Dump(false)]
            public ushort UShortProperty { get; set; }
            [Dump(-1)]
            public uint UIntProperty { get; set; }
            [Dump(-2)]
            public ulong ULongProperty { get; set; }
            [Dump(-3, ValueFormat="{0:F01}")]
            public double DoubleProperty { get; set; }
            [Dump(-4, ValueFormat="{0:F01}")]
            public float FloatProperty { get; set; }
            [Dump(-5)]
            public decimal DecimalProperty { get; set; }
            [Dump(-6)]
            public Guid GuidProperty { get; set; }
            [Dump(-7)]
            public Uri UriProperty { get; set; }
            [Dump(-8)]
            public DateTime DateTimeProperty { get; set; }
            [Dump(-9, ValueFormat="ToString()")]
            public DateTime DateTimeProperty1 { get; set; }
            [Dump(-10)]
            public TimeSpan TimeSpanProperty { get; set; }
            [Dump(false)]
            public DateTimeOffset DateTimeOffsetProperty { get; set; }
        }

        [MetadataType(typeof(Object3Metadata))]
        public class Object3
        {
            public object ObjectProperty { get; set; }
            public int? NullIntProperty { get; set; }
            public long? NullLongProperty { get; set; }
            public bool BoolProperty { get; set; }
            public char CharProperty { get; set; }
            public byte ByteProperty { get; set; }
            public sbyte SByteProperty { get; set; }
            public short ShortProperty { get; set; }
            public int IntProperty { get; set; }
            public long LongProperty { get; set; }
            public ushort UShortProperty { get; set; }
            public uint UIntProperty { get; set; }
            public ulong ULongProperty { get; set; }
            public double DoubleProperty { get; set; }
            public float FloatProperty { get; set; }
            public decimal DecimalProperty { get; set; }
            public Guid GuidProperty { get; set; }
            public Uri UriProperty { get; set; }
            public DateTime DateTimeProperty { get; set; }
            public TimeSpan TimeSpanProperty { get; set; }
            public DateTimeOffset DateTimeOffsetProperty { get; set; }
        }

        [Dump(DumpNullValues=ShouldDump.Skip)]
        class Object3Metadata
        {
            private Object3Metadata() { }

            [Dump(0, LabelFormat="{0,-24} : ")]
            public object ObjectProperty { get; set; }
            [Dump(1)]
            public object NullIntProperty { get; set; }
            [Dump(2)]
            public object NullLongProperty { get; set; }
            [Dump(3)]
            public object BoolProperty { get; set; }
            [Dump(4)]
            public object CharProperty { get; set; }
            [Dump(5)]
            public object ByteProperty { get; set; }
            [Dump(6)]
            public object SByteProperty { get; set; }
            [Dump(7)]
            public object ShortProperty { get; set; }
            [Dump(8)]
            public object IntProperty { get; set; }
            [Dump(9)]
            public object LongProperty { get; set; }
            [Dump(false)]
            public object UShortProperty { get; set; }
            [Dump(-1)]
            public object UIntProperty { get; set; }
            [Dump(-2)]
            public object ULongProperty { get; set; }
            [Dump(-3, ValueFormat="{0:F01}")]
            public object DoubleProperty { get; set; }
            [Dump(-4, ValueFormat="{0:F01}")]
            public object FloatProperty { get; set; }
            [Dump(-5)]
            public object DecimalProperty { get; set; }
            [Dump(-6)]
            public object GuidProperty { get; set; }
            [Dump(-7)]
            public object UriProperty { get; set; }
            [Dump(-8)]
            public object DateTimeProperty { get; set; }
            [Dump(-9)]
            public object TimeSpanProperty { get; set; }
            [Dump(false)]
            public object DateTimeOffsetProperty { get; set; }
        }

        internal static Object1 GetObject1()
        {
            return new Object1
            {
                ObjectProperty         = null,
                NullIntProperty        = null,
                NullLongProperty       = 1L,
                BoolProperty           = true,
                CharProperty           = 'A',
                ByteProperty           = (byte)1,
                SByteProperty          = (sbyte)1,
                ShortProperty          = (short)1,
                IntProperty            = (int)1,
                LongProperty           = (long)1,
                UShortProperty         = (ushort)1,
                UIntProperty           = (uint)1,
                ULongProperty          = (ulong)1,
                DoubleProperty         = 1.0,
                FloatProperty          = (float)1.0,
                DecimalProperty        = 1M,
                GuidProperty           = Guid.Empty,
                UriProperty            = new Uri("http://localhost"),
                DateTimeProperty       = new DateTime(2013, 1, 13),
                TimeSpanProperty       = new TimeSpan(123L),
                DateTimeOffsetProperty = new DateTimeOffset(new DateTime(2013, 1, 13)),
            };
        }

        internal static Object2 GetObject2()
        {
            return new Object2
            {
                ObjectProperty         = null,
                NullIntProperty        = null,
                NullLongProperty       = 1L,
                BoolProperty           = true,
                CharProperty           = 'A',
                ByteProperty           = (byte)1,
                SByteProperty          = (sbyte)1,
                ShortProperty          = (short)1,
                IntProperty            = (int)1,
                LongProperty           = (long)1,
                UShortProperty         = (ushort)1,
                UIntProperty           = (uint)1,
                ULongProperty          = (ulong)1,
                DoubleProperty         = 1.0,
                FloatProperty          = (float)1.0,
                DecimalProperty        = 1M,
                GuidProperty           = Guid.Empty,
                UriProperty            = new Uri("http://localhost"),
                DateTimeProperty       = new DateTime(2013, 1, 13),
                DateTimeProperty1      = new DateTime(2013, 1, 25, 11, 23, 45),
                TimeSpanProperty       = new TimeSpan(123L),
                DateTimeOffsetProperty = new DateTimeOffset(new DateTime(2013, 1, 13)),
            };
        }

        internal static Object3 GetObject3()
        {
            return new Object3
            {
                ObjectProperty         = null,
                NullIntProperty        = null,
                NullLongProperty       = 1L,
                BoolProperty           = true,
                CharProperty           = 'A',
                ByteProperty           = (byte)1,
                SByteProperty          = (sbyte)1,
                ShortProperty          = (short)1,
                IntProperty            = (int)1,
                LongProperty           = (long)1,
                UShortProperty         = (ushort)1,
                UIntProperty           = (uint)1,
                ULongProperty          = (ulong)1,
                DoubleProperty         = 1.0,
                FloatProperty          = (float)1.0,
                DecimalProperty        = 1M,
                GuidProperty           = Guid.Empty,
                UriProperty            = new Uri("http://localhost"),
                DateTimeProperty       = new DateTime(2013, 1, 13),
                TimeSpanProperty       = new TimeSpan(123L),
                DateTimeOffsetProperty = new DateTimeOffset(new DateTime(2013, 1, 13)),
            };
        }

        [TestMethod]
        public void TestDumpObject1_1()
        {
            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var target = new ObjectTextDumper(w);
                var expected = @"
Object1 (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+Object1, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
  BoolProperty             = True
  ByteProperty             = 1
  CharProperty             = A
  DateTimeOffsetProperty   = 2013-01-13T00:00:00.0000000-05:00
  DateTimeProperty         = 2013-01-13T00:00:00.0000000
  DecimalProperty          = 1
  DoubleProperty           = 1
  FloatProperty            = 1
  GuidProperty             = 00000000-0000-0000-0000-000000000000
  IntProperty              = 1
  LongProperty             = 1
  NullIntProperty          = <null>
  NullLongProperty         = 1
  ObjectProperty           = <null>
  SByteProperty            = 1
  ShortProperty            = 1
  TimeSpanProperty         = 00:00:00.0000123
  UIntProperty             = 1
  ULongProperty            = 1
  UShortProperty           = 1
  UriProperty              = http://localhost/";

                target.Dump(GetObject1());

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        [TestMethod]
        public void TestDumpObject1_2()
        {
            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var target = new ObjectTextDumper(w);
                var expected = @"
Object1 (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+Object1, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
  ObjectProperty           : <null>
  NullIntProperty          = <null>
  NullLongProperty         = 1
  BoolProperty             = True
  CharProperty             = A
  ByteProperty             = 1
  SByteProperty            = 1
  ShortProperty            = 1
  IntProperty              = 1
  LongProperty             = 1
  UIntProperty             = 1
  ULongProperty            = 1
  DoubleProperty           = 1.0
  FloatProperty            = 1.0
  DecimalProperty          = 1
  GuidProperty             = 00000000-0000-0000-0000-000000000000
  UriProperty              = http://localhost/
  DateTimeProperty         = 2013-01-13T00:00:00.0000000
  TimeSpanProperty         = 00:00:00.0000123";

                target.Dump(GetObject1(), typeof(Object1Metadata), null);

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        static string expected123part = @" 
  NullLongProperty         = 1
  BoolProperty             = True
  CharProperty             = A
  ByteProperty             = 1
  SByteProperty            = 1
  ShortProperty            = 1
  IntProperty              = 1
  LongProperty             = 1
  UIntProperty             = 1
  ULongProperty            = 1
  DoubleProperty           = 1.0
  FloatProperty            = 1.0
  DecimalProperty          = 1
  GuidProperty             = 00000000-0000-0000-0000-000000000000
  UriProperty              = http://localhost/
  DateTimeProperty         = 2013-01-13T00:00:00.0000000
  TimeSpanProperty         = 00:00:00.0000123";

        [TestMethod]
        public void TestDumpObject1_3()
        {
            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var target = new ObjectTextDumper(w);
                var expected = @"
Object1 (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+Object1, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf):" + expected123part;

                target.Dump(GetObject1(), typeof(Object1Metadata), new DumpAttribute { DumpNullValues = ShouldDump.Skip });

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        [TestMethod]
        public void TestDumpObject2_1()
        {
            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var target = new ObjectTextDumper(w);
                var expected = @"
Object2 (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+Object2, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
  NullLongProperty         = 1
  BoolProperty             = True
  CharProperty             = A
  ByteProperty             = 1
  SByteProperty            = 1
  ShortProperty            = 1
  IntProperty              = 1
  LongProperty             = 1
  UIntProperty             = 1
  ULongProperty            = 1
  DoubleProperty           = 1.0
  FloatProperty            = 1.0
  DecimalProperty          = 1
  GuidProperty             = 00000000-0000-0000-0000-000000000000
  UriProperty              = http://localhost/
  DateTimeProperty         = 2013-01-13T00:00:00.0000000
  DateTimeProperty1        = 1/25/2013 11:23:45 AM
  TimeSpanProperty         = 00:00:00.0000123";

                target.Dump(GetObject2());

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        [TestMethod]
        public void TestDumpObject3_1()
        {
            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var target = new ObjectTextDumper(w);
                var expected = @"
Object3 (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+Object3, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf):" + expected123part;

                target.Dump(GetObject3());

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        class Object4_1
        {
            public Object4_1()
            {
                Property1 = "Property1";
                Property2 = "Property2";
            }

            public string Property1 { get; set; }
            public string Property2 { get; set; }
        }

        class Object5_1
        {
            public Object5_1()
            {
                PropertyA = "PropertyA";
                PropertyB = "PropertyB";
                Associate = new Object4_1();
                Associate2 = new Object4_1();
            }

            [Dump(0)]
            public string PropertyA { get; set; }

            [Dump(1)]
            public string PropertyB { get; set; }

            [Dump(RecurseDump=ShouldDump.Skip, DefaultProperty="Property1")]
            public Object4_1 Associate { get; set; }

            [Dump(RecurseDump=ShouldDump.Skip)]
            public Object4_1 Associate2 { get; set; }
        }

        [TestMethod]
        public void TestDumpObject5_1()
        {
            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var target = new ObjectTextDumper(w);
                var expected = @"
Object5_1 (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+Object5_1, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
  PropertyA                = PropertyA
  PropertyB                = PropertyB
  Associate                = Object4_1 (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+Object4_1, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
    Property1                = Property1
  Associate2               = Object4_1 (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+Object4_1, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): ";

                target.Dump(new Object5_1());

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        static bool TestMethod(int a)
        {
            return a < 0;
        }

        [TestMethod]
        public void TestDumpDBNull()
        {
            var dbnull = DBNull.Value;

            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var target = new ObjectTextDumper(w);
                var expected = @"DBNull";

                target.Dump(dbnull);

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        [TestMethod]
        public void TestDumpExpression()
        {
            Expression<Func<int, int>> expression = a => 5;

            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var target = new ObjectTextDumper(w);
                var expected = @"
Expression`1 (System.Linq.Expressions.Expression`1[[System.Func`2[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089): 
  NodeType                 = ExpressionType.Lambda
  Name                     = <null>
  ReturnType               = (TypeInfo): System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
  Parameters               = <ParameterExpression>[1]: (System.Runtime.CompilerServices.TrueReadOnlyCollection`1[[System.Linq.Expressions.ParameterExpression, System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
    PrimitiveParameterExpression`1 (System.Linq.Expressions.PrimitiveParameterExpression`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089): 
      NodeType                 = ExpressionType.Parameter
      Name                     = a
      IsByRef                  = False
      Type                     = (TypeInfo): System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
      CanReduce                = False
  Body                     = ConstantExpression (System.Linq.Expressions.ConstantExpression, System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089): 
    NodeType                 = ExpressionType.Constant
    Value                    = 5
    Type                     = (TypeInfo): System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
    CanReduce                = False
  Type                     = (TypeInfo): System.Func`2[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
  TailCall                 = False
  CanReduce                = False";

                target.Dump(expression);

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        class Object6
        {
            public Object6()
            {
                Property1 = "Property1";
                Property2 = "Property2";
                d = TestMethod;
                ex = p => p > 0;
            }

            public string Property1 { get; set; }
            public string Property2 { get; set; }
            public Func<int, bool> d { get; set; }

            [Dump(ValueFormat="ToString()")]
            public Expression<Func<int, bool>> ex { get; set; }
        }

        [TestMethod]
        public void TestDumpObject6_1()
        {
            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var target = new ObjectTextDumper(w);
                var expected = @"
Object6 (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+Object6, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
  Property1                = Property1
  Property2                = Property2
  d                        = ObjectTextDumperTest.TestMethod (static)
  ex                       = p => (p > 0)";

                target.Dump(new Object6());

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        class Object7
        {
            public Object7()
            {
                Property1 = "Property1";
                Property2 = "Property2";
                Array = new[] { 0, 1, 2, 3, 4, 5, };
            }

            public string Property1 { get; set; }
            public string Property2 { get; set; }
            public int[] Array { get; set; }
        }

        [TestMethod]
        public void TestDumpObject7_1()
        {
            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var target = new ObjectTextDumper(w);
                var expected = @"
Object7 (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+Object7, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
  Array                    = <Int32>[6]: (System.Int32[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
    0
    1
    2
    3
    4
    5
  Property1                = Property1
  Property2                = Property2";

                target.Dump(new Object7());

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        [TestMethod]
        public void TestDumpObject7_2()
        {
            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var target = new ObjectTextDumper(w);
                var test = new Object7
                {
                    Array = new[] { 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, },
                };
                var expected = @"
Object7 (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+Object7, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
  Array                    = <Int32>[18]: (System.Int32[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
    0
    1
    2
    3
    4
    5
    0
    1
    2
    3
    ... dumped the first 10 elements.
  Property1                = Property1
  Property2                = Property2";

                target.Dump(test);

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        class Object8
        {
            public Object8()
            {
                Property1 = "Property1";
                Property2 = "Property2";
                Array = new[] { 0, 1, 2, 3, 4, 5, };
                Array2 = new[] { 0, 1, 2, 3, 4, 5, };
                Array3 = new byte[] { 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, };
                Array4 = new byte[] { 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, };
                Array5 = new byte[] { 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, };
                Array6 = new ArrayList { 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, };
            }

            public string Property1 { get; set; }
            public string Property2 { get; set; }
            [Dump(MaxLength=3)]
            public int[] Array { get; set; }
            [Dump(RecurseDump=ShouldDump.Skip)]
            public int[] Array2 { get; set; }
            [Dump(MaxLength=-1)]
            public byte[] Array3 { get; set; }
            [Dump()]
            public byte[] Array4 { get; set; }
            [Dump(MaxLength=3)]
            public byte[] Array5 { get; set; }
            [Dump(MaxLength=3)]
            public ArrayList Array6 { get; set; }
        }

        [TestMethod]
        public void TestDumpObject8_1()
        {
            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var target = new ObjectTextDumper(w);
                var expected = @"
Object8 (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+Object8, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
  Array                    = <Int32>[6]: (System.Int32[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
    0
    1
    2
    ... dumped the first 3 elements.
  Array2                   = <Int32>[6]: (System.Int32[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
  Array3                   = <Byte>[18]: 00-01-02-03-04-05-00-01-02-03-04-05-00-01-02-03-04-05
  Array4                   = <Byte>[18]: 00-01-02-03-04-05-00-01-02-03... dumped the first 10 elements.
  Array5                   = <Byte>[18]: 00-01-02... dumped the first 3 elements.
  Array6                   = <Object>[18]: (System.Collections.ArrayList, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
    0
    1
    2
    ... dumped the first 3 elements.
  Property1                = Property1
  Property2                = Property2";

                target.Dump(new Object8());

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        class Object7_1
        {
            public Object7_1()
            {
                Property1 = "Property1";
                Property2 = "Property2";
                List = new List<int> { 0, 1, 2, 3, 4, 5, };
            }

            public string Property1 { get; set; }
            public string Property2 { get; set; }
            public List<int> List { get; set; }
        }

        [TestMethod]
        public void TestDumpObject7_1_1()
        {
            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var target = new ObjectTextDumper(w);
                var expected = @"
Object7_1 (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+Object7_1, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
  List                     = <Int32>[6]: (System.Collections.Generic.List`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
    0
    1
    2
    3
    4
    5
  Property1                = Property1
  Property2                = Property2";

                target.Dump(new Object7_1());

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        [TestMethod]
        public void TestDumpObject7_1_2()
        {
            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var target = new ObjectTextDumper(w);
                var test = new Object7_1
                {
                    List = new List<int> { 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, },
                };
                var expected = @"
Object7_1 (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+Object7_1, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
  List                     = <Int32>[18]: (System.Collections.Generic.List`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
    0
    1
    2
    3
    4
    5
    0
    1
    2
    3
    ... dumped the first 10 elements.
  Property1                = Property1
  Property2                = Property2";

                target.Dump(test);

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        class Object8_1
        {
            public Object8_1()
            {
                Property1 = "Property1";
                Property2 = "Property2";
                List = new[] { 0, 1, 2, 3, 4, 5, };
                List2 = new[] { 0, 1, 2, 3, 4, 5, };
            }

            public string Property1 { get; set; }
            public string Property2 { get; set; }
            [Dump(MaxLength=3)]
            public int[] List { get; set; }
            [Dump(RecurseDump=ShouldDump.Skip)]
            public int[] List2 { get; set; }
        }

        [TestMethod]
        public void TestDumpObject8_1_1()
        {
            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var target = new ObjectTextDumper(w);
                var expected = @"
Object8_1 (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+Object8_1, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
  List                     = <Int32>[6]: (System.Int32[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
    0
    1
    2
    ... dumped the first 3 elements.
  List2                    = <Int32>[6]: (System.Int32[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
  Property1                = Property1
  Property2                = Property2";

                target.Dump(new Object8_1());

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        class Object90
        {
            public TestEnum Prop { get; set; }
        }

        class Object9
        {
            public Object9()
            {
                Object90 = new Object90 { Prop=TestEnum.One };
                Prop91 = 0;
                Prop92 = 1;
                Prop93 = 4;
                Prop94 = 5;
            }

            [Dump(0)]
            public Object90 Object90 { get; set; }
            [Dump(1)]
            public int Prop91 { get; set; }
            [Dump(2)]
            public int Prop92 { get; set; }
            [Dump(-1)]
            public int Prop93 { get; set; }
            [Dump(-2)]
            public int Prop94 { get; set; }
            public string this[int index]
            {
                get { return index.ToString(); }
                set { }
            }
        }

        class Object91 : Object9
        {
            public Object91()
            {
                Prop911 = 2;
                Prop912 = 3;
                Prop913 = 6;
                Prop914 = 7;
            }

            [Dump(0)]
            public int Prop911 { get; set; }
            [Dump(1)]
            public int Prop912 { get; set; }
            [Dump(-1)]
            public int Prop913 { get; set; }
            [Dump(-2)]
            public int Prop914 { get; set; }
            [Dump(-3)]
            public Object90 InheritedObject90 { get { return Object90; } }
        }

        [TestMethod]
        public void TestDumpObject9_1()
        {
            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var target = new ObjectTextDumper(w);
                var expected = @"
Object91 (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+Object91, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
  Object90                 = Object90 (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+Object90, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
    Prop                     = TestEnum.One
  Prop91                   = 0
  Prop92                   = 1
  Prop911                  = 2
  Prop912                  = 3
  Prop93                   = 4
  Prop94                   = 5
  Prop913                  = 6
  Prop914                  = 7
  InheritedObject90        = Object90 (see above)";

                target.Dump(new Object91());

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        public class Object10
        {
            public int Offset { get; set; }
            public static string Static(int param) { return param.ToString(); }
            public string Instance(int param) { return (Offset+param).ToString(); }
        }

        public class ObjectWithDelegates
        {
            public ObjectWithDelegates()
            {
                Object10Prop = new Object10 { Offset = 23 };
                DelegateProp1 = Object10.Static;
                DelegateProp2 = Object10Prop.Instance;
                DelegateProp3 = TestMethod;
            }

            public Object10 Object10Prop { get; set; }
            public Func<int, string> DelegateProp0 { get; set; }
            public Func<int, string> DelegateProp1 { get; set; }
            public Func<int, string> DelegateProp2 { get; set; }
            public Func<int, bool> DelegateProp3 { get; set; }
        }

        [TestMethod]
        public void TestDumpObjectWithDelegates()
        {
            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var target = new ObjectTextDumper(w);
                var expected = @"
ObjectWithDelegates (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+ObjectWithDelegates, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
  DelegateProp0            = <null>
  DelegateProp1            = Object10.Static (static)
  DelegateProp2            = Object10.Instance (instance)
  DelegateProp3            = ObjectTextDumperTest.TestMethod (static)
  Object10Prop             = Object10 (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+Object10, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
    Offset                   = 23";

                target.Dump(new ObjectWithDelegates());

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        class MyEnumerable : IEnumerable
        {
            [Dump(true)]
            List<int> List { get; set; }

            public MyEnumerable()
            {
                List = new List<int> { 0, 1, 3, };
            }

            public string Property { get; set; }

            #region IEnumerable Members
            public IEnumerator GetEnumerator()
            {
                return List.GetEnumerator();
            }
            #endregion
        }

        class ObjectWithMyEnumerable
        {
            public ObjectWithMyEnumerable()
            {
                Stuff = "stuff";
                MyEnumerable = new MyEnumerable();
            }

            public string Stuff { get; set; }
            public MyEnumerable MyEnumerable { get; set; }
        }

        [TestMethod]
        public void TestDumpObjectWithMyEnumerable()
        {
            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var target = new ObjectTextDumper(w);
                var expected = @"
ObjectWithMyEnumerable (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+ObjectWithMyEnumerable, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
  MyEnumerable             = MyEnumerable (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+MyEnumerable, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
    List                     = <Int32>[3]: (System.Collections.Generic.List`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
      0
      1
      3
    Property                 = <null>
  Stuff                    = stuff";

                target.Dump(new ObjectWithMyEnumerable());

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        class ObjectWithMembers
        {
            public int Property { get; set; }
            public void Method1()
            {
            }
            public int Method2(int a)
            {
                return a;
            }
            public string Method3(int a, int b)
            {
                return (a+b).ToString();
            }
            public T Method4<T>(int a, int b) where T : new()
            {
                return new T();
            }
            public T Method5<T, U>(int a, int b) where T : new()
            {
                return new T();
            }
            public string this[int index]
            {
                get { return index.ToString(); }
            }
            public string this[string index, int index1]
            {
                get { return index+index1; }
                set { }
            }
            public event EventHandler Event;
            public int member;
        }

        class ObjectWithMemberInfos
        {
            public Type Type { get; set; }
            public PropertyInfo PropertyInfo { get; set; }
            public PropertyInfo IndexerIntInfo { get; set; }
            public PropertyInfo IndexerStringInfo { get; set; }
            public MethodInfo Method1Info { get; set; }
            public MethodInfo Method2Info { get; set; }
            public MethodInfo Method3Info { get; set; }
            public MethodInfo Method4Info { get; set; }
            public MethodInfo Method5Info { get; set; }
            public EventInfo EventInfo { get; set; }
            public MemberInfo MemberInfo { get; set; }
            public MemberInfo[] MemberInfos { get; set; }

            public ObjectWithMemberInfos()
            {
                Type = typeof(ObjectWithMembers);
                foreach (var pi in Type.GetProperties())
                {
                    if (pi.Name == "Property")
                        PropertyInfo = pi;
                    else
                        if (pi.GetIndexParameters()[0].ParameterType == typeof(int))
                            IndexerIntInfo = pi;
                        else
                            if (pi.GetIndexParameters()[0].ParameterType == typeof(string))
                                IndexerStringInfo = pi;
                }
                Method1Info = Type.GetMethod("Method1");
                Method2Info = Type.GetMethod("Method2");
                Method3Info = Type.GetMethod("Method3");
                Method4Info = Type.GetMethod("Method4");
                Method5Info = Type.GetMethod("Method5");
                EventInfo = Type.GetEvent("Event");
                MemberInfo = Type.GetMember("member")[0];
                MemberInfos = Type.GetMembers();
            }
        }

        [TestMethod]
        public void TestDumpObjectWithMemberInfos()
        {
            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var target = new ObjectTextDumper(w);
                var expected = @"
ObjectWithMemberInfos (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+ObjectWithMemberInfos, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
  EventInfo                = (Event): EventHandler ObjectWithMembers.Event
  IndexerIntInfo           = (Property): String ObjectWithMembers.this[Int32] { get; }
  IndexerStringInfo        = (Property): String ObjectWithMembers.this[String, Int32] { get;set; }
  MemberInfo               = (Field): Int32 ObjectWithMembers.member
  MemberInfos              = <MemberInfo>[22]: (System.Reflection.MemberInfo[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
    (Method): Int32 ObjectWithMembers.get_Property()
    (Method): Void ObjectWithMembers.set_Property(Int32 value)
    (Method): Void ObjectWithMembers.Method1()
    (Method): Int32 ObjectWithMembers.Method2(Int32 a)
    (Method): String ObjectWithMembers.Method3(Int32 a, Int32 b)
    (Method): T ObjectWithMembers.Method4<T>(Int32 a, Int32 b)
    (Method): T ObjectWithMembers.Method5<T, U>(Int32 a, Int32 b)
    (Method): String ObjectWithMembers.get_Item(Int32 index)
    (Method): String ObjectWithMembers.get_Item(String index, Int32 index1)
    (Method): Void ObjectWithMembers.set_Item(String index, Int32 index1, String value)
    ... dumped the first 10 elements.
  Method1Info              = (Method): Void ObjectWithMembers.Method1()
  Method2Info              = (Method): Int32 ObjectWithMembers.Method2(Int32 a)
  Method3Info              = (Method): String ObjectWithMembers.Method3(Int32 a, Int32 b)
  Method4Info              = (Method): T ObjectWithMembers.Method4<T>(Int32 a, Int32 b)
  Method5Info              = (Method): T ObjectWithMembers.Method5<T, U>(Int32 a, Int32 b)
  PropertyInfo             = (Property): Int32 ObjectWithMembers.Property { get;set; }
  Type                     = (NestedType): vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+ObjectWithMembers, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf";

                target.Dump(new ObjectWithMemberInfos());

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        [Dump(MaxDepth=3)]
        class NestedItem
        {
            public int Property { get; set; }
            public NestedItem Next { get; set; }
        }

        [TestMethod]
        public void TestDumpNestedObject()
        {
            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var test = new NestedItem
                {
                    Property = 0,
                    Next = new NestedItem
                    {
                        Property = 1,
                        Next = new NestedItem
                        {
                            Property = 2,
                            Next = new NestedItem
                            {
                                Property = 3,
                                Next = new NestedItem
                                {
                                    Property = 4,
                                    Next = null,
                                }
                            }
                        },
                    },
                };
                var target = new ObjectTextDumper(w);
                var expected = @"
NestedItem (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+NestedItem, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
  Next                     = NestedItem (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+NestedItem, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
    Next                     = NestedItem (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+NestedItem, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
      Next                     = NestedItem (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+NestedItem, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
        Next                     = ...object dump reached the maximum depth level. Use the DumpAttribute.MaxDepth to increase the depth level if needed.
        Property                 = 3
      Property                 = 2
    Property                 = 1
  Property                 = 0";

                target.Dump(test);

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        [TestMethod]
        public void TestCollectionObject()
        {
            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var test = new List<string> { "one", "two", "three" };
                var target = new ObjectTextDumper(w);
                var expected = @"
<String>[3]: (System.Collections.Generic.List`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
  one
  two
  three";

                target.Dump(test);

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        class Base
        {
            public virtual string StringProperty { get; set; }
            public int IntProperty { get; set; }
        }

        class Derived : Base
        {
            public override string StringProperty { get; set; }
            public new int IntProperty { get; set; }
        }

        [TestMethod]
        public void TestVirtualProperties()
        {
            var test = new Derived
            {
                StringProperty = "StringProperty",
                IntProperty    = 5,
            };
            var expected = @"
Derived (vm.Aspects.Tests.Diagnostics.ObjectTextDumperTest+Derived, vm.Aspects.Tests.Diagnostics, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
  IntProperty              = 0
  StringProperty           = StringProperty
  IntProperty              = 5";

            using (var w = new StringWriter(CultureInfo.InvariantCulture))
            {
                var target = new ObjectTextDumper(w);

                target.Dump(test);

                var actual = w.GetStringBuilder().ToString();

                TestContext.WriteLine("{0}", actual);
                Assert.AreEqual(expected, actual);
            }
        }

        T GetSandboxedObject<T>(SecurityZone zone)
        {
            // get the permission set:
            Evidence evidence = new Evidence();

            evidence.AddHostEvidence(new Zone(zone));

            var permissionSet = SecurityManager.GetStandardSandbox(evidence);

            // create the app domain:
            AppDomainSetup setup = new AppDomainSetup();

            setup.ApplicationBase = Path.GetDirectoryName(typeof(T).Assembly.Location);

            AppDomain domain = AppDomain.CreateDomain(
                                    "TestSandbox",
                                    null,
                                    setup,
                                    permissionSet);

            //create a remote instance of the T class
            return (T)Activator.CreateInstanceFrom(
                                    domain,
                                    typeof(T).Assembly.ManifestModule.FullyQualifiedName,
                                    typeof(T).FullName)
                               .Unwrap();
        }

        [TestMethod]
        public void TestCallFromSandbox()
        {
            Object1Dump od = GetSandboxedObject<Object1Dump>(SecurityZone.Internet);

            var actual = od.DumpObject1();
            var expected = @"
vm.Aspects.Tests.PartialTrustStub.Object1 (Note: The caller does not have the permission to use reflection. Therefore System.Object.ToString() on the object has been dumped instead.)";

            TestContext.WriteLine("{0}", actual);
            Assert.AreEqual(expected, actual);
        }

        [TestMethod]
        public void TestCallFromSandbox1()
        {
            var actual = GetSandboxedObject<Object1Dump>(SecurityZone.MyComputer).DumpObject1();
            var expected = @"
Object1 (vm.Aspects.Tests.PartialTrustStub.Object1, vm.Aspects.Tests.PartialTrustStub, Version=0.9.3.7, Culture=neutral, PublicKeyToken=02c332ac00257baf): 
  BoolProperty             = True
  ByteProperty             = 1
  CharProperty             = A
  DateTimeOffsetProperty   = 2013-01-13T00:00:00.0000000-05:00
  DateTimeProperty         = 2013-01-13T00:00:00.0000000
  DecimalProperty          = 1
  DoubleProperty           = 1
  FloatProperty            = 1
  GuidProperty             = 00000000-0000-0000-0000-000000000000
  IntProperty              = 1
  LongProperty             = 1
  NullIntProperty          = <null>
  NullLongProperty         = 1
  ObjectProperty           = <null>
  SByteProperty            = 1
  ShortProperty            = 1
  TimeSpanProperty         = 00:00:00.0000123
  UIntProperty             = 1
  ULongProperty            = 1
  UShortProperty           = 1
  UriProperty              = http://localhost/";

            TestContext.WriteLine("{0}", actual);
            Assert.AreEqual(expected, actual);
        }
    }
}